package edu.neumont.pro180._2009_q2.chess;

import java.awt.Point;
import java.util.ArrayList;
import java.util.Collection;

import edu.neumont.pro180._2009_q2.GamePiece;
import edu.neumont.pro180._2009_q2.GamePieceMove;
import edu.neumont.pro180._2009_q2.Square;

public class Pawn extends GamePiece {
	private final int value = 1;
	
	@Override
	public int getValue() {
		return value;
	}
	
	/**
	 * Note that hasMoved is NEVER changed inside the Pawn class. Instead, it's
	 * changed in the PawnFirstMove class, which is a special type of Move that
	 * manipulates the Pawn's hasMoved property. This enables the move to be
	 * done or undone without the Pawn's logic tracking its state.
	 */
	private boolean hasMoved = false;
	private Point direction;
	private Square originalSquare = null;

	/**
	 * Constructor for a Pawn that is on the team of some Player.
	 * 
	 * @param initialDirection
	 *            The initial direction that the pawn is traveling: (0,1) for
	 *            white, (0,-1) for black.
	 */
	public Pawn(Point initialDirection) {
		this.direction = initialDirection;
	}

	@Override
	public Collection<Square> getPossibleSquares() {
		ArrayList<Square> squares = new ArrayList<Square>();
		Square squareInFront = this.getPosition().getNeighbor(this.direction);
		if (squareInFront != null && !squareInFront.hasGamePiece()) {
			squares.add(squareInFront);
			// The next square in front is only valid if the pawn has never
			// moved.
			if (this.getPosition() == this.originalSquare) {
				Square twoSquaresInFront = squareInFront
						.getNeighbor(this.direction);
				if (twoSquaresInFront != null
						&& !twoSquaresInFront.hasGamePiece()) {
					squares.add(twoSquaresInFront);
				}
			}
		}
		// Find the attack points to the front left and front right.
		Point frontLeft = new Point(-1, this.direction.y);
		Point frontRight = new Point(1, this.direction.y);
		if (canAttack(frontLeft)) {
			squares.add(this.getPosition().getNeighbor(frontLeft));
		}
		if (canAttack(frontRight)) {
			squares.add(this.getPosition().getNeighbor(frontRight));
		}
		return squares;
	}

	/**
	 * Tells if there is a piece to attack in the direction specified.
	 * 
	 * @param direction
	 *            The direction to find a piece to attack.
	 * @return Whether there is a piece to attack in that direction.
	 */
	private boolean canAttack(Point direction) {
		boolean toReturn = false;
		Square attackSquare = this.getSquare().getNeighbor(direction);
		if ((null != attackSquare)
				&& attackSquare.hasGamePiece()
				&& (attackSquare.getGamePiece().getPlayer() != this.getPlayer())) {
			toReturn = true;
		}
		return toReturn;
	}

	@Override
	public boolean setPosition(Square square) {
		if (this.originalSquare == null && square != null) {
			this.originalSquare = square;
		}
		return super.setPosition(square);
	}

	@Override
	public GamePieceMove getMove(Square destination) {
		if (hasMoved && this.getPosition() != this.originalSquare) {
			return new GamePieceMove(this, destination);
		} else {
			return new PawnFirstMove(this, destination);
		}
	}

	/**
	 * Helper class for the Pawn class. It should ONLY be created on the first
	 * move of a pawn, and it will manipulate the Pawn's hasMoved property.
	 * 
	 * @author Tyler Young
	 * 
	 */
	private class PawnFirstMove extends GamePieceMove {
		public PawnFirstMove(Pawn toMove, Square destination) {
			super(toMove, destination);
		}

		@Override
		public void execute() {
			Pawn pawn = (Pawn) toMove;
			Square originalSquare = pawn.getPosition();
			super.execute();
			if (originalSquare != pawn.originalSquare) {
				pawn.hasMoved = true;
			}
		}

		@Override
		public void undo() {
			super.undo();
			((Pawn) this.toMove).hasMoved = false;
		}
	}
}
